home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / python2.6 / urllib.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  53.4 KB  |  1,993 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''Open an arbitrary URL.
  5.  
  6. See the following document for more info on URLs:
  7. "Names and Addresses, URIs, URLs, URNs, URCs", at
  8. http://www.w3.org/pub/WWW/Addressing/Overview.html
  9.  
  10. See also the HTTP spec (from which the error codes are derived):
  11. "HTTP - Hypertext Transfer Protocol", at
  12. http://www.w3.org/pub/WWW/Protocols/
  13.  
  14. Related standards and specs:
  15. - RFC1808: the "relative URL" spec. (authoritative status)
  16. - RFC1738 - the "URL standard". (authoritative status)
  17. - RFC1630 - the "URI spec". (informational status)
  18.  
  19. The object returned by URLopener().open(file) will differ per
  20. protocol.  All you know is that is has methods read(), readline(),
  21. readlines(), fileno(), close() and info().  The read*(), fileno()
  22. and close() methods work like those of open files.
  23. The info() method returns a mimetools.Message object which can be
  24. used to query various info about the object, if available.
  25. (mimetools.Message objects are queried with the getheader() method.)
  26. '''
  27. import string
  28. import socket
  29. import os
  30. import time
  31. import sys
  32. from urlparse import urljoin as basejoin
  33. import warnings
  34. __all__ = [
  35.     'urlopen',
  36.     'URLopener',
  37.     'FancyURLopener',
  38.     'urlretrieve',
  39.     'urlcleanup',
  40.     'quote',
  41.     'quote_plus',
  42.     'unquote',
  43.     'unquote_plus',
  44.     'urlencode',
  45.     'url2pathname',
  46.     'pathname2url',
  47.     'splittag',
  48.     'localhost',
  49.     'thishost',
  50.     'ftperrors',
  51.     'basejoin',
  52.     'unwrap',
  53.     'splittype',
  54.     'splithost',
  55.     'splituser',
  56.     'splitpasswd',
  57.     'splitport',
  58.     'splitnport',
  59.     'splitquery',
  60.     'splitattr',
  61.     'splitvalue',
  62.     'getproxies']
  63. __version__ = '1.17'
  64. MAXFTPCACHE = 10
  65. if os.name == 'mac':
  66.     from macurl2path import url2pathname, pathname2url
  67. elif os.name == 'nt':
  68.     from nturl2path import url2pathname, pathname2url
  69. elif os.name == 'riscos':
  70.     from rourl2path import url2pathname, pathname2url
  71. else:
  72.     
  73.     def url2pathname(pathname):
  74.         """OS-specific conversion from a relative URL of the 'file' scheme
  75.         to a file system path; not recommended for general use."""
  76.         return unquote(pathname)
  77.  
  78.     
  79.     def pathname2url(pathname):
  80.         """OS-specific conversion from a file system path to a relative URL
  81.         of the 'file' scheme; not recommended for general use."""
  82.         return quote(pathname)
  83.  
  84. _urlopener = None
  85.  
  86. def urlopen(url, data = None, proxies = None):
  87.     '''Create a file-like object for the specified URL to read from.'''
  88.     global _urlopener
  89.     warnpy3k = warnpy3k
  90.     import warnings
  91.     warnings.warnpy3k('urllib.urlopen() has been removed in Python 3.0 in favor of urllib2.urlopen()', stacklevel = 2)
  92.     if proxies is not None:
  93.         opener = FancyURLopener(proxies = proxies)
  94.     elif not _urlopener:
  95.         opener = FancyURLopener()
  96.         _urlopener = opener
  97.     else:
  98.         opener = _urlopener
  99.     if data is None:
  100.         return opener.open(url)
  101.     return opener.open(url, data)
  102.  
  103.  
  104. def urlretrieve(url, filename = None, reporthook = None, data = None):
  105.     global _urlopener
  106.     if not _urlopener:
  107.         _urlopener = FancyURLopener()
  108.     
  109.     return _urlopener.retrieve(url, filename, reporthook, data)
  110.  
  111.  
  112. def urlcleanup():
  113.     if _urlopener:
  114.         _urlopener.cleanup()
  115.     
  116.  
  117.  
  118. try:
  119.     import ssl
  120. except:
  121.     _have_ssl = False
  122.  
  123. _have_ssl = True
  124.  
  125. class ContentTooShortError(IOError):
  126.     
  127.     def __init__(self, message, content):
  128.         IOError.__init__(self, message)
  129.         self.content = content
  130.  
  131.  
  132. ftpcache = { }
  133.  
  134. class URLopener:
  135.     """Class to open URLs.
  136.     This is a class rather than just a subroutine because we may need
  137.     more than one set of global protocol-specific options.
  138.     Note -- this is a base class for those who don't want the
  139.     automatic handling of errors type 302 (relocated) and 401
  140.     (authorization needed)."""
  141.     __tempfiles = None
  142.     version = 'Python-urllib/%s' % __version__
  143.     
  144.     def __init__(self, proxies = None, **x509):
  145.         if proxies is None:
  146.             proxies = getproxies()
  147.         
  148.         if not hasattr(proxies, 'has_key'):
  149.             raise AssertionError, 'proxies must be a mapping'
  150.         self.proxies = proxies
  151.         self.key_file = x509.get('key_file')
  152.         self.cert_file = x509.get('cert_file')
  153.         self.addheaders = [
  154.             ('User-Agent', self.version)]
  155.         self._URLopener__tempfiles = []
  156.         self._URLopener__unlink = os.unlink
  157.         self.tempcache = None
  158.         self.ftpcache = ftpcache
  159.  
  160.     
  161.     def __del__(self):
  162.         self.close()
  163.  
  164.     
  165.     def close(self):
  166.         self.cleanup()
  167.  
  168.     
  169.     def cleanup(self):
  170.         if self._URLopener__tempfiles:
  171.             for file in self._URLopener__tempfiles:
  172.                 
  173.                 try:
  174.                     self._URLopener__unlink(file)
  175.                 continue
  176.                 except OSError:
  177.                     continue
  178.                 
  179.  
  180.             
  181.             del self._URLopener__tempfiles[:]
  182.         
  183.         if self.tempcache:
  184.             self.tempcache.clear()
  185.         
  186.  
  187.     
  188.     def addheader(self, *args):
  189.         """Add a header to be used by the HTTP interface only
  190.         e.g. u.addheader('Accept', 'sound/basic')"""
  191.         self.addheaders.append(args)
  192.  
  193.     
  194.     def open(self, fullurl, data = None):
  195.         """Use URLopener().open(file) instead of open(file, 'r')."""
  196.         fullurl = unwrap(toBytes(fullurl))
  197.         if self.tempcache and fullurl in self.tempcache:
  198.             (filename, headers) = self.tempcache[fullurl]
  199.             fp = open(filename, 'rb')
  200.             return addinfourl(fp, headers, fullurl)
  201.         (urltype, url) = splittype(fullurl)
  202.         if not urltype:
  203.             urltype = 'file'
  204.         
  205.         if urltype in self.proxies:
  206.             proxy = self.proxies[urltype]
  207.             (urltype, proxyhost) = splittype(proxy)
  208.             (host, selector) = splithost(proxyhost)
  209.             url = (host, fullurl)
  210.         else:
  211.             proxy = None
  212.         name = 'open_' + urltype
  213.         self.type = urltype
  214.         name = name.replace('-', '_')
  215.         if not hasattr(self, name):
  216.             if proxy:
  217.                 return self.open_unknown_proxy(proxy, fullurl, data)
  218.             return self.open_unknown(fullurl, data)
  219.         hasattr(self, name)
  220.         
  221.         try:
  222.             if data is None:
  223.                 return getattr(self, name)(url)
  224.             return getattr(self, name)(url, data)
  225.         except socket.error:
  226.             msg = None
  227.             raise IOError, ('socket error', msg), sys.exc_info()[2]
  228.  
  229.  
  230.     
  231.     def open_unknown(self, fullurl, data = None):
  232.         '''Overridable interface to open unknown URL type.'''
  233.         (type, url) = splittype(fullurl)
  234.         raise IOError, ('url error', 'unknown url type', type)
  235.  
  236.     
  237.     def open_unknown_proxy(self, proxy, fullurl, data = None):
  238.         '''Overridable interface to open unknown URL type.'''
  239.         (type, url) = splittype(fullurl)
  240.         raise IOError, ('url error', 'invalid proxy for %s' % type, proxy)
  241.  
  242.     
  243.     def retrieve(self, url, filename = None, reporthook = None, data = None):
  244.         '''retrieve(url) returns (filename, headers) for a local object
  245.         or (tempfilename, headers) for a remote object.'''
  246.         url = unwrap(toBytes(url))
  247.         if self.tempcache and url in self.tempcache:
  248.             return self.tempcache[url]
  249.         (type, url1) = splittype(url)
  250.         if filename is None:
  251.             if not type or type == 'file':
  252.                 
  253.                 try:
  254.                     fp = self.open_local_file(url1)
  255.                     hdrs = fp.info()
  256.                     del fp
  257.                     return (url2pathname(splithost(url1)[1]), hdrs)
  258.                 except IOError:
  259.                     url in self.tempcache
  260.                     msg = url in self.tempcache
  261.                 except:
  262.                     url in self.tempcache<EXCEPTION MATCH>IOError
  263.                 
  264.  
  265.         url in self.tempcache
  266.         fp = self.open(url, data)
  267.         
  268.         try:
  269.             headers = fp.info()
  270.             if filename:
  271.                 tfp = open(filename, 'wb')
  272.             else:
  273.                 import tempfile
  274.                 (garbage, path) = splittype(url)
  275.                 if not path:
  276.                     pass
  277.                 (garbage, path) = splithost('')
  278.                 if not path:
  279.                     pass
  280.                 (path, garbage) = splitquery('')
  281.                 if not path:
  282.                     pass
  283.                 (path, garbage) = splitattr('')
  284.                 suffix = os.path.splitext(path)[1]
  285.                 (fd, filename) = tempfile.mkstemp(suffix)
  286.                 self._URLopener__tempfiles.append(filename)
  287.                 tfp = os.fdopen(fd, 'wb')
  288.             
  289.             try:
  290.                 result = (filename, headers)
  291.                 if self.tempcache is not None:
  292.                     self.tempcache[url] = result
  293.                 
  294.                 bs = 8192
  295.                 size = -1
  296.                 read = 0
  297.                 blocknum = 0
  298.                 if reporthook:
  299.                     if 'content-length' in headers:
  300.                         size = int(headers['Content-Length'])
  301.                     
  302.                     reporthook(blocknum, bs, size)
  303.                 
  304.                 while None:
  305.                     block = fp.read(bs)
  306.                     if block == '':
  307.                         break
  308.                     
  309.                     read += len(block)
  310.                     blocknum += 1
  311.                     if reporthook:
  312.                         reporthook(blocknum, bs, size)
  313.                         continue
  314.                 tfp.close()
  315.             finally:
  316.                 fp.close()
  317.  
  318.             del fp
  319.             del tfp
  320.             if size >= 0 and read < size:
  321.                 raise ContentTooShortError('retrieval incomplete: got only %i out of %i bytes' % (read, size), result)
  322.             read < size
  323.             return result
  324.  
  325.  
  326.     
  327.     def open_http(self, url, data = None):
  328.         '''Use HTTP protocol.'''
  329.         import httplib
  330.         user_passwd = None
  331.         proxy_passwd = None
  332.         if isinstance(url, str):
  333.             (host, selector) = splithost(url)
  334.             if host:
  335.                 (user_passwd, host) = splituser(host)
  336.                 host = unquote(host)
  337.             
  338.             realhost = host
  339.         else:
  340.             (host, selector) = url
  341.             (proxy_passwd, host) = splituser(host)
  342.             (urltype, rest) = splittype(selector)
  343.             url = rest
  344.             user_passwd = None
  345.             if urltype.lower() != 'http':
  346.                 realhost = None
  347.             else:
  348.                 (realhost, rest) = splithost(rest)
  349.                 if realhost:
  350.                     (user_passwd, realhost) = splituser(realhost)
  351.                 
  352.                 if user_passwd:
  353.                     selector = '%s://%s%s' % (urltype, realhost, rest)
  354.                 
  355.                 if proxy_bypass(realhost):
  356.                     host = realhost
  357.                 
  358.         if not host:
  359.             raise IOError, ('http error', 'no host given')
  360.         host
  361.         if proxy_passwd:
  362.             import base64
  363.             proxy_auth = base64.b64encode(proxy_passwd).strip()
  364.         else:
  365.             proxy_auth = None
  366.         if user_passwd:
  367.             import base64
  368.             auth = base64.b64encode(user_passwd).strip()
  369.         else:
  370.             auth = None
  371.         h = httplib.HTTP(host)
  372.         if data is not None:
  373.             h.putrequest('POST', selector)
  374.             h.putheader('Content-Type', 'application/x-www-form-urlencoded')
  375.             h.putheader('Content-Length', '%d' % len(data))
  376.         else:
  377.             h.putrequest('GET', selector)
  378.         if proxy_auth:
  379.             h.putheader('Proxy-Authorization', 'Basic %s' % proxy_auth)
  380.         
  381.         if auth:
  382.             h.putheader('Authorization', 'Basic %s' % auth)
  383.         
  384.         if realhost:
  385.             h.putheader('Host', realhost)
  386.         
  387.         for args in self.addheaders:
  388.             h.putheader(*args)
  389.         
  390.         h.endheaders()
  391.         if data is not None:
  392.             h.send(data)
  393.         
  394.         (errcode, errmsg, headers) = h.getreply()
  395.         fp = h.getfile()
  396.         if errcode == -1:
  397.             if fp:
  398.                 fp.close()
  399.             
  400.             raise IOError, ('http protocol error', 0, 'got a bad status line', None)
  401.         errcode == -1
  402.         if errcode <= errcode:
  403.             pass
  404.         elif errcode < 300:
  405.             return addinfourl(fp, headers, 'http:' + url, errcode)
  406.         if data is None:
  407.             return self.http_error(url, fp, errcode, errmsg, headers)
  408.         return self.http_error(url, fp, errcode, errmsg, headers, data)
  409.  
  410.     
  411.     def http_error(self, url, fp, errcode, errmsg, headers, data = None):
  412.         '''Handle http errors.
  413.         Derived class can override this, or provide specific handlers
  414.         named http_error_DDD where DDD is the 3-digit error code.'''
  415.         name = 'http_error_%d' % errcode
  416.         if hasattr(self, name):
  417.             method = getattr(self, name)
  418.             if data is None:
  419.                 result = method(url, fp, errcode, errmsg, headers)
  420.             else:
  421.                 result = method(url, fp, errcode, errmsg, headers, data)
  422.             if result:
  423.                 return result
  424.         
  425.         return self.http_error_default(url, fp, errcode, errmsg, headers)
  426.  
  427.     
  428.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  429.         '''Default error handler: close the connection and raise IOError.'''
  430.         void = fp.read()
  431.         fp.close()
  432.         raise IOError, ('http error', errcode, errmsg, headers)
  433.  
  434.     if _have_ssl:
  435.         
  436.         def open_https(self, url, data = None):
  437.             '''Use HTTPS protocol.'''
  438.             import httplib
  439.             user_passwd = None
  440.             proxy_passwd = None
  441.             if isinstance(url, str):
  442.                 (host, selector) = splithost(url)
  443.                 if host:
  444.                     (user_passwd, host) = splituser(host)
  445.                     host = unquote(host)
  446.                 
  447.                 realhost = host
  448.             else:
  449.                 (host, selector) = url
  450.                 (proxy_passwd, host) = splituser(host)
  451.                 (urltype, rest) = splittype(selector)
  452.                 url = rest
  453.                 user_passwd = None
  454.                 if urltype.lower() != 'https':
  455.                     realhost = None
  456.                 else:
  457.                     (realhost, rest) = splithost(rest)
  458.                     if realhost:
  459.                         (user_passwd, realhost) = splituser(realhost)
  460.                     
  461.                     if user_passwd:
  462.                         selector = '%s://%s%s' % (urltype, realhost, rest)
  463.                     
  464.             if not host:
  465.                 raise IOError, ('https error', 'no host given')
  466.             host
  467.             if proxy_passwd:
  468.                 import base64
  469.                 proxy_auth = base64.b64encode(proxy_passwd).strip()
  470.             else:
  471.                 proxy_auth = None
  472.             if user_passwd:
  473.                 import base64
  474.                 auth = base64.b64encode(user_passwd).strip()
  475.             else:
  476.                 auth = None
  477.             h = httplib.HTTPS(host, 0, key_file = self.key_file, cert_file = self.cert_file)
  478.             if data is not None:
  479.                 h.putrequest('POST', selector)
  480.                 h.putheader('Content-Type', 'application/x-www-form-urlencoded')
  481.                 h.putheader('Content-Length', '%d' % len(data))
  482.             else:
  483.                 h.putrequest('GET', selector)
  484.             if proxy_auth:
  485.                 h.putheader('Proxy-Authorization', 'Basic %s' % proxy_auth)
  486.             
  487.             if auth:
  488.                 h.putheader('Authorization', 'Basic %s' % auth)
  489.             
  490.             if realhost:
  491.                 h.putheader('Host', realhost)
  492.             
  493.             for args in self.addheaders:
  494.                 h.putheader(*args)
  495.             
  496.             h.endheaders()
  497.             if data is not None:
  498.                 h.send(data)
  499.             
  500.             (errcode, errmsg, headers) = h.getreply()
  501.             fp = h.getfile()
  502.             if errcode == -1:
  503.                 if fp:
  504.                     fp.close()
  505.                 
  506.                 raise IOError, ('http protocol error', 0, 'got a bad status line', None)
  507.             errcode == -1
  508.             if errcode <= errcode:
  509.                 pass
  510.             elif errcode < 300:
  511.                 return addinfourl(fp, headers, 'https:' + url, errcode)
  512.             if data is None:
  513.                 return self.http_error(url, fp, errcode, errmsg, headers)
  514.             return self.http_error(url, fp, errcode, errmsg, headers, data)
  515.  
  516.     
  517.     
  518.     def open_file(self, url):
  519.         '''Use local file or FTP depending on form of URL.'''
  520.         if not isinstance(url, str):
  521.             raise IOError, ('file error', 'proxy support for file protocol currently not implemented')
  522.         isinstance(url, str)
  523.         if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/':
  524.             return self.open_ftp(url)
  525.         return self.open_local_file(url)
  526.  
  527.     
  528.     def open_local_file(self, url):
  529.         '''Use local file.'''
  530.         import mimetypes
  531.         import mimetools
  532.         import email.utils as email
  533.         
  534.         try:
  535.             StringIO = StringIO
  536.             import cStringIO
  537.         except ImportError:
  538.             StringIO = StringIO
  539.             import StringIO
  540.  
  541.         (host, file) = splithost(url)
  542.         localname = url2pathname(file)
  543.         
  544.         try:
  545.             stats = os.stat(localname)
  546.         except OSError:
  547.             e = None
  548.             raise IOError(e.errno, e.strerror, e.filename)
  549.  
  550.         size = stats.st_size
  551.         modified = email.utils.formatdate(stats.st_mtime, usegmt = True)
  552.         mtype = mimetypes.guess_type(url)[0]
  553.         if not mtype:
  554.             pass
  555.         headers = mimetools.Message(StringIO('Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % ('text/plain', size, modified)))
  556.         if not host:
  557.             urlfile = file
  558.             if file[:1] == '/':
  559.                 urlfile = 'file://' + file
  560.             
  561.             return addinfourl(open(localname, 'rb'), headers, urlfile)
  562.         (host, port) = splitport(host)
  563.         if not port and socket.gethostbyname(host) in (localhost(), thishost()):
  564.             urlfile = file
  565.             if file[:1] == '/':
  566.                 urlfile = 'file://' + file
  567.             
  568.             return addinfourl(open(localname, 'rb'), headers, urlfile)
  569.         raise IOError, ('local file error', 'not on local host')
  570.  
  571.     
  572.     def open_ftp(self, url):
  573.         '''Use FTP protocol.'''
  574.         if not isinstance(url, str):
  575.             raise IOError, ('ftp error', 'proxy support for ftp protocol currently not implemented')
  576.         isinstance(url, str)
  577.         import mimetypes
  578.         import mimetools
  579.         
  580.         try:
  581.             StringIO = StringIO
  582.             import cStringIO
  583.         except ImportError:
  584.             StringIO = StringIO
  585.             import StringIO
  586.  
  587.         (host, path) = splithost(url)
  588.         if not host:
  589.             raise IOError, ('ftp error', 'no host given')
  590.         host
  591.         (host, port) = splitport(host)
  592.         (user, host) = splituser(host)
  593.         if user:
  594.             (user, passwd) = splitpasswd(user)
  595.         else:
  596.             passwd = None
  597.         host = unquote(host)
  598.         if not user:
  599.             pass
  600.         user = unquote('')
  601.         if not passwd:
  602.             pass
  603.         passwd = unquote('')
  604.         host = socket.gethostbyname(host)
  605.         if not port:
  606.             import ftplib
  607.             port = ftplib.FTP_PORT
  608.         else:
  609.             port = int(port)
  610.         (path, attrs) = splitattr(path)
  611.         path = unquote(path)
  612.         dirs = path.split('/')
  613.         dirs = dirs[:-1]
  614.         file = dirs[-1]
  615.         if dirs and not dirs[0]:
  616.             dirs = dirs[1:]
  617.         
  618.         if dirs and not dirs[0]:
  619.             dirs[0] = '/'
  620.         
  621.         key = (user, host, port, '/'.join(dirs))
  622.         if len(self.ftpcache) > MAXFTPCACHE:
  623.             for k in self.ftpcache.keys():
  624.                 if k != key:
  625.                     v = self.ftpcache[k]
  626.                     del self.ftpcache[k]
  627.                     v.close()
  628.                     continue
  629.             
  630.         
  631.         
  632.         try:
  633.             if key not in self.ftpcache:
  634.                 self.ftpcache[key] = ftpwrapper(user, passwd, host, port, dirs)
  635.             
  636.             if not file:
  637.                 type = 'D'
  638.             else:
  639.                 type = 'I'
  640.             for attr in attrs:
  641.                 (attr, value) = splitvalue(attr)
  642.                 if attr.lower() == 'type' and value in ('a', 'A', 'i', 'I', 'd', 'D'):
  643.                     type = value.upper()
  644.                     continue
  645.             
  646.             (fp, retrlen) = self.ftpcache[key].retrfile(file, type)
  647.             mtype = mimetypes.guess_type('ftp:' + url)[0]
  648.             headers = ''
  649.             if mtype:
  650.                 headers += 'Content-Type: %s\n' % mtype
  651.             
  652.             if retrlen is not None and retrlen >= 0:
  653.                 headers += 'Content-Length: %d\n' % retrlen
  654.             
  655.             headers = mimetools.Message(StringIO(headers))
  656.             return addinfourl(fp, headers, 'ftp:' + url)
  657.         except ftperrors():
  658.             msg = None
  659.             raise IOError, ('ftp error', msg), sys.exc_info()[2]
  660.  
  661.  
  662.     
  663.     def open_data(self, url, data = None):
  664.         '''Use "data" URL.'''
  665.         if not isinstance(url, str):
  666.             raise IOError, ('data error', 'proxy support for data protocol currently not implemented')
  667.         isinstance(url, str)
  668.         import mimetools
  669.         
  670.         try:
  671.             StringIO = StringIO
  672.             import cStringIO
  673.         except ImportError:
  674.             StringIO = StringIO
  675.             import StringIO
  676.  
  677.         
  678.         try:
  679.             (type, data) = url.split(',', 1)
  680.         except ValueError:
  681.             raise IOError, ('data error', 'bad data URL')
  682.  
  683.         if not type:
  684.             type = 'text/plain;charset=US-ASCII'
  685.         
  686.         semi = type.rfind(';')
  687.         if semi >= 0 and '=' not in type[semi:]:
  688.             encoding = type[semi + 1:]
  689.             type = type[:semi]
  690.         else:
  691.             encoding = ''
  692.         msg = []
  693.         msg.append('Date: %s' % time.strftime('%a, %d %b %Y %T GMT', time.gmtime(time.time())))
  694.         msg.append('Content-type: %s' % type)
  695.         if encoding == 'base64':
  696.             import base64
  697.             data = base64.decodestring(data)
  698.         else:
  699.             data = unquote(data)
  700.         msg.append('Content-Length: %d' % len(data))
  701.         msg.append('')
  702.         msg.append(data)
  703.         msg = '\n'.join(msg)
  704.         f = StringIO(msg)
  705.         headers = mimetools.Message(f, 0)
  706.         return addinfourl(f, headers, url)
  707.  
  708.  
  709.  
  710. class FancyURLopener(URLopener):
  711.     '''Derived class with handlers for errors we can handle (perhaps).'''
  712.     
  713.     def __init__(self, *args, **kwargs):
  714.         URLopener.__init__(self, *args, **kwargs)
  715.         self.auth_cache = { }
  716.         self.tries = 0
  717.         self.maxtries = 10
  718.  
  719.     
  720.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  721.         """Default error handling -- don't raise an exception."""
  722.         return addinfourl(fp, headers, 'http:' + url, errcode)
  723.  
  724.     
  725.     def http_error_302(self, url, fp, errcode, errmsg, headers, data = None):
  726.         '''Error 302 -- relocated (temporarily).'''
  727.         self.tries += 1
  728.         if self.maxtries and self.tries >= self.maxtries:
  729.             if hasattr(self, 'http_error_500'):
  730.                 meth = self.http_error_500
  731.             else:
  732.                 meth = self.http_error_default
  733.             self.tries = 0
  734.             return meth(url, fp, 500, 'Internal Server Error: Redirect Recursion', headers)
  735.         result = self.redirect_internal(url, fp, errcode, errmsg, headers, data)
  736.         self.tries = 0
  737.         return result
  738.  
  739.     
  740.     def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
  741.         if 'location' in headers:
  742.             newurl = headers['location']
  743.         elif 'uri' in headers:
  744.             newurl = headers['uri']
  745.         else:
  746.             return None
  747.         void = ('location' in headers).read()
  748.         fp.close()
  749.         newurl = basejoin(self.type + ':' + url, newurl)
  750.         return self.open(newurl)
  751.  
  752.     
  753.     def http_error_301(self, url, fp, errcode, errmsg, headers, data = None):
  754.         '''Error 301 -- also relocated (permanently).'''
  755.         return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  756.  
  757.     
  758.     def http_error_303(self, url, fp, errcode, errmsg, headers, data = None):
  759.         '''Error 303 -- also relocated (essentially identical to 302).'''
  760.         return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  761.  
  762.     
  763.     def http_error_307(self, url, fp, errcode, errmsg, headers, data = None):
  764.         '''Error 307 -- relocated, but turn POST into error.'''
  765.         if data is None:
  766.             return self.http_error_302(url, fp, errcode, errmsg, headers, data)
  767.         return self.http_error_default(url, fp, errcode, errmsg, headers)
  768.  
  769.     
  770.     def http_error_401(self, url, fp, errcode, errmsg, headers, data = None):
  771.         '''Error 401 -- authentication required.
  772.         This function supports Basic authentication only.'''
  773.         if 'www-authenticate' not in headers:
  774.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  775.         
  776.         stuff = headers['www-authenticate']
  777.         import re
  778.         match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff)
  779.         if not match:
  780.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  781.         
  782.         (scheme, realm) = match.groups()
  783.         if scheme.lower() != 'basic':
  784.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  785.         
  786.         name = 'retry_' + self.type + '_basic_auth'
  787.         if data is None:
  788.             return getattr(self, name)(url, realm)
  789.         return getattr(self, name)(url, realm, data)
  790.  
  791.     
  792.     def http_error_407(self, url, fp, errcode, errmsg, headers, data = None):
  793.         '''Error 407 -- proxy authentication required.
  794.         This function supports Basic authentication only.'''
  795.         if 'proxy-authenticate' not in headers:
  796.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  797.         
  798.         stuff = headers['proxy-authenticate']
  799.         import re
  800.         match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff)
  801.         if not match:
  802.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  803.         
  804.         (scheme, realm) = match.groups()
  805.         if scheme.lower() != 'basic':
  806.             URLopener.http_error_default(self, url, fp, errcode, errmsg, headers)
  807.         
  808.         name = 'retry_proxy_' + self.type + '_basic_auth'
  809.         if data is None:
  810.             return getattr(self, name)(url, realm)
  811.         return getattr(self, name)(url, realm, data)
  812.  
  813.     
  814.     def retry_proxy_http_basic_auth(self, url, realm, data = None):
  815.         (host, selector) = splithost(url)
  816.         newurl = 'http://' + host + selector
  817.         proxy = self.proxies['http']
  818.         (urltype, proxyhost) = splittype(proxy)
  819.         (proxyhost, proxyselector) = splithost(proxyhost)
  820.         i = proxyhost.find('@') + 1
  821.         proxyhost = proxyhost[i:]
  822.         (user, passwd) = self.get_user_passwd(proxyhost, realm, i)
  823.         if not user or passwd:
  824.             return None
  825.         proxyhost = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + proxyhost
  826.         self.proxies['http'] = 'http://' + proxyhost + proxyselector
  827.         if data is None:
  828.             return self.open(newurl)
  829.         return self.open(newurl, data)
  830.  
  831.     
  832.     def retry_proxy_https_basic_auth(self, url, realm, data = None):
  833.         (host, selector) = splithost(url)
  834.         newurl = 'https://' + host + selector
  835.         proxy = self.proxies['https']
  836.         (urltype, proxyhost) = splittype(proxy)
  837.         (proxyhost, proxyselector) = splithost(proxyhost)
  838.         i = proxyhost.find('@') + 1
  839.         proxyhost = proxyhost[i:]
  840.         (user, passwd) = self.get_user_passwd(proxyhost, realm, i)
  841.         if not user or passwd:
  842.             return None
  843.         proxyhost = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + proxyhost
  844.         self.proxies['https'] = 'https://' + proxyhost + proxyselector
  845.         if data is None:
  846.             return self.open(newurl)
  847.         return self.open(newurl, data)
  848.  
  849.     
  850.     def retry_http_basic_auth(self, url, realm, data = None):
  851.         (host, selector) = splithost(url)
  852.         i = host.find('@') + 1
  853.         host = host[i:]
  854.         (user, passwd) = self.get_user_passwd(host, realm, i)
  855.         if not user or passwd:
  856.             return None
  857.         host = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + host
  858.         newurl = 'http://' + host + selector
  859.         if data is None:
  860.             return self.open(newurl)
  861.         return self.open(newurl, data)
  862.  
  863.     
  864.     def retry_https_basic_auth(self, url, realm, data = None):
  865.         (host, selector) = splithost(url)
  866.         i = host.find('@') + 1
  867.         host = host[i:]
  868.         (user, passwd) = self.get_user_passwd(host, realm, i)
  869.         if not user or passwd:
  870.             return None
  871.         host = quote(user, safe = '') + ':' + quote(passwd, safe = '') + '@' + host
  872.         newurl = 'https://' + host + selector
  873.         if data is None:
  874.             return self.open(newurl)
  875.         return self.open(newurl, data)
  876.  
  877.     
  878.     def get_user_passwd(self, host, realm, clear_cache = 0):
  879.         key = realm + '@' + host.lower()
  880.         if key in self.auth_cache:
  881.             if clear_cache:
  882.                 del self.auth_cache[key]
  883.             else:
  884.                 return self.auth_cache[key]
  885.         clear_cache
  886.         (user, passwd) = self.prompt_user_passwd(host, realm)
  887.         if user or passwd:
  888.             self.auth_cache[key] = (user, passwd)
  889.         
  890.         return (user, passwd)
  891.  
  892.     
  893.     def prompt_user_passwd(self, host, realm):
  894.         '''Override this in a GUI environment!'''
  895.         import getpass
  896.         
  897.         try:
  898.             user = raw_input('Enter username for %s at %s: ' % (realm, host))
  899.             passwd = getpass.getpass('Enter password for %s in %s at %s: ' % (user, realm, host))
  900.             return (user, passwd)
  901.         except KeyboardInterrupt:
  902.             print 
  903.             return (None, None)
  904.  
  905.  
  906.  
  907. _localhost = None
  908.  
  909. def localhost():
  910.     """Return the IP address of the magic hostname 'localhost'."""
  911.     global _localhost
  912.     if _localhost is None:
  913.         _localhost = socket.gethostbyname('localhost')
  914.     
  915.     return _localhost
  916.  
  917. _thishost = None
  918.  
  919. def thishost():
  920.     '''Return the IP address of the current host.'''
  921.     global _thishost
  922.     if _thishost is None:
  923.         _thishost = socket.gethostbyname(socket.gethostname())
  924.     
  925.     return _thishost
  926.  
  927. _ftperrors = None
  928.  
  929. def ftperrors():
  930.     '''Return the set of errors raised by the FTP class.'''
  931.     global _ftperrors
  932.     if _ftperrors is None:
  933.         import ftplib
  934.         _ftperrors = ftplib.all_errors
  935.     
  936.     return _ftperrors
  937.  
  938. _noheaders = None
  939.  
  940. def noheaders():
  941.     '''Return an empty mimetools.Message object.'''
  942.     global _noheaders
  943.     if _noheaders is None:
  944.         import mimetools
  945.         
  946.         try:
  947.             StringIO = StringIO
  948.             import cStringIO
  949.         except ImportError:
  950.             StringIO = StringIO
  951.             import StringIO
  952.  
  953.         _noheaders = mimetools.Message(StringIO(), 0)
  954.         _noheaders.fp.close()
  955.     
  956.     return _noheaders
  957.  
  958.  
  959. class ftpwrapper:
  960.     '''Class used by open_ftp() for cache of open FTP connections.'''
  961.     
  962.     def __init__(self, user, passwd, host, port, dirs, timeout = socket._GLOBAL_DEFAULT_TIMEOUT):
  963.         self.user = user
  964.         self.passwd = passwd
  965.         self.host = host
  966.         self.port = port
  967.         self.dirs = dirs
  968.         self.timeout = timeout
  969.         self.init()
  970.  
  971.     
  972.     def init(self):
  973.         import ftplib
  974.         self.busy = 0
  975.         self.ftp = ftplib.FTP()
  976.         self.ftp.connect(self.host, self.port, self.timeout)
  977.         self.ftp.login(self.user, self.passwd)
  978.         for dir in self.dirs:
  979.             self.ftp.cwd(dir)
  980.         
  981.  
  982.     
  983.     def retrfile(self, file, type):
  984.         import ftplib
  985.         self.endtransfer()
  986.         if type in ('d', 'D'):
  987.             cmd = 'TYPE A'
  988.             isdir = 1
  989.         else:
  990.             cmd = 'TYPE ' + type
  991.             isdir = 0
  992.         
  993.         try:
  994.             self.ftp.voidcmd(cmd)
  995.         except ftplib.all_errors:
  996.             self.init()
  997.             self.ftp.voidcmd(cmd)
  998.  
  999.         conn = None
  1000.         if file and not isdir:
  1001.             
  1002.             try:
  1003.                 cmd = 'RETR ' + file
  1004.                 conn = self.ftp.ntransfercmd(cmd)
  1005.             except ftplib.error_perm:
  1006.                 reason = None
  1007.                 if str(reason)[:3] != '550':
  1008.                     raise IOError, ('ftp error', reason), sys.exc_info()[2]
  1009.                 str(reason)[:3] != '550'
  1010.             except:
  1011.                 None<EXCEPTION MATCH>ftplib.error_perm
  1012.             
  1013.  
  1014.         None<EXCEPTION MATCH>ftplib.error_perm
  1015.         if not conn:
  1016.             self.ftp.voidcmd('TYPE A')
  1017.             if file:
  1018.                 pwd = self.ftp.pwd()
  1019.                 
  1020.                 try:
  1021.                     self.ftp.cwd(file)
  1022.                 except ftplib.error_perm:
  1023.                     reason = None
  1024.                     raise IOError, ('ftp error', reason), sys.exc_info()[2]
  1025.                 finally:
  1026.                     self.ftp.cwd(pwd)
  1027.  
  1028.                 cmd = 'LIST ' + file
  1029.             else:
  1030.                 cmd = 'LIST'
  1031.             conn = self.ftp.ntransfercmd(cmd)
  1032.         
  1033.         self.busy = 1
  1034.         return (addclosehook(conn[0].makefile('rb'), self.endtransfer), conn[1])
  1035.  
  1036.     
  1037.     def endtransfer(self):
  1038.         if not self.busy:
  1039.             return None
  1040.         self.busy = 0
  1041.         
  1042.         try:
  1043.             self.ftp.voidresp()
  1044.         except ftperrors():
  1045.             self.busy
  1046.             self.busy
  1047.         except:
  1048.             self.busy
  1049.  
  1050.  
  1051.     
  1052.     def close(self):
  1053.         self.endtransfer()
  1054.         
  1055.         try:
  1056.             self.ftp.close()
  1057.         except ftperrors():
  1058.             pass
  1059.  
  1060.  
  1061.  
  1062.  
  1063. class addbase:
  1064.     '''Base class for addinfo and addclosehook.'''
  1065.     
  1066.     def __init__(self, fp):
  1067.         self.fp = fp
  1068.         self.read = self.fp.read
  1069.         self.readline = self.fp.readline
  1070.         if hasattr(self.fp, 'readlines'):
  1071.             self.readlines = self.fp.readlines
  1072.         
  1073.         if hasattr(self.fp, 'fileno'):
  1074.             self.fileno = self.fp.fileno
  1075.         else:
  1076.             
  1077.             self.fileno = lambda : pass
  1078.         if hasattr(self.fp, '__iter__'):
  1079.             self.__iter__ = self.fp.__iter__
  1080.             if hasattr(self.fp, 'next'):
  1081.                 self.next = self.fp.next
  1082.             
  1083.         
  1084.  
  1085.     
  1086.     def __repr__(self):
  1087.         return '<%s at %r whose fp = %r>' % (self.__class__.__name__, id(self), self.fp)
  1088.  
  1089.     
  1090.     def close(self):
  1091.         self.read = None
  1092.         self.readline = None
  1093.         self.readlines = None
  1094.         self.fileno = None
  1095.         if self.fp:
  1096.             self.fp.close()
  1097.         
  1098.         self.fp = None
  1099.  
  1100.  
  1101.  
  1102. class addclosehook(addbase):
  1103.     '''Class to add a close hook to an open file.'''
  1104.     
  1105.     def __init__(self, fp, closehook, *hookargs):
  1106.         addbase.__init__(self, fp)
  1107.         self.closehook = closehook
  1108.         self.hookargs = hookargs
  1109.  
  1110.     
  1111.     def close(self):
  1112.         addbase.close(self)
  1113.         if self.closehook:
  1114.             self.closehook(*self.hookargs)
  1115.             self.closehook = None
  1116.             self.hookargs = None
  1117.         
  1118.  
  1119.  
  1120.  
  1121. class addinfo(addbase):
  1122.     '''class to add an info() method to an open file.'''
  1123.     
  1124.     def __init__(self, fp, headers):
  1125.         addbase.__init__(self, fp)
  1126.         self.headers = headers
  1127.  
  1128.     
  1129.     def info(self):
  1130.         return self.headers
  1131.  
  1132.  
  1133.  
  1134. class addinfourl(addbase):
  1135.     '''class to add info() and geturl() methods to an open file.'''
  1136.     
  1137.     def __init__(self, fp, headers, url, code = None):
  1138.         addbase.__init__(self, fp)
  1139.         self.headers = headers
  1140.         self.url = url
  1141.         self.code = code
  1142.  
  1143.     
  1144.     def info(self):
  1145.         return self.headers
  1146.  
  1147.     
  1148.     def getcode(self):
  1149.         return self.code
  1150.  
  1151.     
  1152.     def geturl(self):
  1153.         return self.url
  1154.  
  1155.  
  1156.  
  1157. try:
  1158.     unicode
  1159. except NameError:
  1160.     
  1161.     def _is_unicode(x):
  1162.         return 0
  1163.  
  1164.  
  1165.  
  1166. def _is_unicode(x):
  1167.     return isinstance(x, unicode)
  1168.  
  1169.  
  1170. def toBytes(url):
  1171.     '''toBytes(u"URL") --> \'URL\'.'''
  1172.     if _is_unicode(url):
  1173.         
  1174.         try:
  1175.             url = url.encode('ASCII')
  1176.         except UnicodeError:
  1177.             raise UnicodeError('URL ' + repr(url) + ' contains non-ASCII characters')
  1178.         except:
  1179.             None<EXCEPTION MATCH>UnicodeError
  1180.         
  1181.  
  1182.     None<EXCEPTION MATCH>UnicodeError
  1183.     return url
  1184.  
  1185.  
  1186. def unwrap(url):
  1187.     """unwrap('<URL:type://host/path>') --> 'type://host/path'."""
  1188.     url = url.strip()
  1189.     if url[:1] == '<' and url[-1:] == '>':
  1190.         url = url[1:-1].strip()
  1191.     
  1192.     if url[:4] == 'URL:':
  1193.         url = url[4:].strip()
  1194.     
  1195.     return url
  1196.  
  1197. _typeprog = None
  1198.  
  1199. def splittype(url):
  1200.     """splittype('type:opaquestring') --> 'type', 'opaquestring'."""
  1201.     global _typeprog
  1202.     if _typeprog is None:
  1203.         import re
  1204.         _typeprog = re.compile('^([^/:]+):')
  1205.     
  1206.     match = _typeprog.match(url)
  1207.     if match:
  1208.         scheme = match.group(1)
  1209.         return (scheme.lower(), url[len(scheme) + 1:])
  1210.     return (None, url)
  1211.  
  1212. _hostprog = None
  1213.  
  1214. def splithost(url):
  1215.     """splithost('//host[:port]/path') --> 'host[:port]', '/path'."""
  1216.     global _hostprog
  1217.     if _hostprog is None:
  1218.         import re
  1219.         _hostprog = re.compile('^//([^/?]*)(.*)$')
  1220.     
  1221.     match = _hostprog.match(url)
  1222.     if match:
  1223.         return match.group(1, 2)
  1224.     return (None, url)
  1225.  
  1226. _userprog = None
  1227.  
  1228. def splituser(host):
  1229.     """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'."""
  1230.     global _userprog
  1231.     if _userprog is None:
  1232.         import re
  1233.         _userprog = re.compile('^(.*)@(.*)$')
  1234.     
  1235.     match = _userprog.match(host)
  1236.     if match:
  1237.         return map(unquote, match.group(1, 2))
  1238.     return (None, host)
  1239.  
  1240. _passwdprog = None
  1241.  
  1242. def splitpasswd(user):
  1243.     """splitpasswd('user:passwd') -> 'user', 'passwd'."""
  1244.     global _passwdprog
  1245.     if _passwdprog is None:
  1246.         import re
  1247.         _passwdprog = re.compile('^([^:]*):(.*)$')
  1248.     
  1249.     match = _passwdprog.match(user)
  1250.     if match:
  1251.         return match.group(1, 2)
  1252.     return (user, None)
  1253.  
  1254. _portprog = None
  1255.  
  1256. def splitport(host):
  1257.     """splitport('host:port') --> 'host', 'port'."""
  1258.     global _portprog
  1259.     if _portprog is None:
  1260.         import re
  1261.         _portprog = re.compile('^(.*):([0-9]+)$')
  1262.     
  1263.     match = _portprog.match(host)
  1264.     if match:
  1265.         return match.group(1, 2)
  1266.     return (host, None)
  1267.  
  1268. _nportprog = None
  1269.  
  1270. def splitnport(host, defport = -1):
  1271.     """Split host and port, returning numeric port.
  1272.     Return given default port if no ':' found; defaults to -1.
  1273.     Return numerical port if a valid number are found after ':'.
  1274.     Return None if ':' but not a valid number."""
  1275.     global _nportprog
  1276.     if _nportprog is None:
  1277.         import re
  1278.         _nportprog = re.compile('^(.*):(.*)$')
  1279.     
  1280.     match = _nportprog.match(host)
  1281.     if match:
  1282.         (host, port) = match.group(1, 2)
  1283.         
  1284.         try:
  1285.             if not port:
  1286.                 raise ValueError, 'no digits'
  1287.             port
  1288.             nport = int(port)
  1289.         except ValueError:
  1290.             nport = None
  1291.  
  1292.         return (host, nport)
  1293.     return (host, defport)
  1294.  
  1295. _queryprog = None
  1296.  
  1297. def splitquery(url):
  1298.     """splitquery('/path?query') --> '/path', 'query'."""
  1299.     global _queryprog
  1300.     if _queryprog is None:
  1301.         import re
  1302.         _queryprog = re.compile('^(.*)\\?([^?]*)$')
  1303.     
  1304.     match = _queryprog.match(url)
  1305.     if match:
  1306.         return match.group(1, 2)
  1307.     return (url, None)
  1308.  
  1309. _tagprog = None
  1310.  
  1311. def splittag(url):
  1312.     """splittag('/path#tag') --> '/path', 'tag'."""
  1313.     global _tagprog
  1314.     if _tagprog is None:
  1315.         import re
  1316.         _tagprog = re.compile('^(.*)#([^#]*)$')
  1317.     
  1318.     match = _tagprog.match(url)
  1319.     if match:
  1320.         return match.group(1, 2)
  1321.     return (url, None)
  1322.  
  1323.  
  1324. def splitattr(url):
  1325.     """splitattr('/path;attr1=value1;attr2=value2;...') ->
  1326.         '/path', ['attr1=value1', 'attr2=value2', ...]."""
  1327.     words = url.split(';')
  1328.     return (words[0], words[1:])
  1329.  
  1330. _valueprog = None
  1331.  
  1332. def splitvalue(attr):
  1333.     """splitvalue('attr=value') --> 'attr', 'value'."""
  1334.     global _valueprog
  1335.     if _valueprog is None:
  1336.         import re
  1337.         _valueprog = re.compile('^([^=]*)=(.*)$')
  1338.     
  1339.     match = _valueprog.match(attr)
  1340.     if match:
  1341.         return match.group(1, 2)
  1342.     return (attr, None)
  1343.  
  1344. _hextochr = dict((lambda .0: for i in .0:
  1345. ('%02x' % i, chr(i)))(range(256)))
  1346. _hextochr.update((lambda .0: for i in .0:
  1347. ('%02X' % i, chr(i)))(range(256)))
  1348.  
  1349. def unquote(s):
  1350.     """unquote('abc%20def') -> 'abc def'."""
  1351.     res = s.split('%')
  1352.     for i in xrange(1, len(res)):
  1353.         item = res[i]
  1354.         
  1355.         try:
  1356.             res[i] = _hextochr[item[:2]] + item[2:]
  1357.         continue
  1358.         except KeyError:
  1359.             res[i] = '%' + item
  1360.             continue
  1361.             except UnicodeDecodeError:
  1362.                 res[i] = unichr(int(item[:2], 16)) + item[2:]
  1363.                 continue
  1364.             
  1365.         return ''.join(res)
  1366.  
  1367.  
  1368.  
  1369. def unquote_plus(s):
  1370.     """unquote('%7e/abc+def') -> '~/abc def'"""
  1371.     s = s.replace('+', ' ')
  1372.     return unquote(s)
  1373.  
  1374. always_safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-'
  1375. _safemaps = { }
  1376.  
  1377. def quote(s, safe = '/'):
  1378.     '''quote(\'abc def\') -> \'abc%20def\'
  1379.  
  1380.     Each part of a URL, e.g. the path info, the query, etc., has a
  1381.     different set of reserved characters that must be quoted.
  1382.  
  1383.     RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists
  1384.     the following reserved characters.
  1385.  
  1386.     reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
  1387.                   "$" | ","
  1388.  
  1389.     Each of these characters is reserved in some component of a URL,
  1390.     but not necessarily in all of them.
  1391.  
  1392.     By default, the quote function is intended for quoting the path
  1393.     section of a URL.  Thus, it will not encode \'/\'.  This character
  1394.     is reserved, but in typical usage the quote function is being
  1395.     called on a path where the existing slash characters are used as
  1396.     reserved characters.
  1397.     '''
  1398.     cachekey = (safe, always_safe)
  1399.     
  1400.     try:
  1401.         safe_map = _safemaps[cachekey]
  1402.     except KeyError:
  1403.         safe += always_safe
  1404.         safe_map = { }
  1405.         for i in range(256):
  1406.             c = chr(i)
  1407.             if not c in safe or c:
  1408.                 pass
  1409.             safe_map[c] = '%%%02X' % i
  1410.         
  1411.         _safemaps[cachekey] = safe_map
  1412.  
  1413.     res = map(safe_map.__getitem__, s)
  1414.     return ''.join(res)
  1415.  
  1416.  
  1417. def quote_plus(s, safe = ''):
  1418.     """Quote the query fragment of a URL; replacing ' ' with '+'"""
  1419.     if ' ' in s:
  1420.         s = quote(s, safe + ' ')
  1421.         return s.replace(' ', '+')
  1422.     return quote(s, safe)
  1423.  
  1424.  
  1425. def urlencode(query, doseq = 0):
  1426.     '''Encode a sequence of two-element tuples or dictionary into a URL query string.
  1427.  
  1428.     If any values in the query arg are sequences and doseq is true, each
  1429.     sequence element is converted to a separate parameter.
  1430.  
  1431.     If the query arg is a sequence of two-element tuples, the order of the
  1432.     parameters in the output will match the order of parameters in the
  1433.     input.
  1434.     '''
  1435.     if hasattr(query, 'items'):
  1436.         query = query.items()
  1437.     else:
  1438.         
  1439.         try:
  1440.             if len(query) and not isinstance(query[0], tuple):
  1441.                 raise TypeError
  1442.             not isinstance(query[0], tuple)
  1443.         except TypeError:
  1444.             (ty, va, tb) = sys.exc_info()
  1445.             raise TypeError, 'not a valid non-string sequence or mapping object', tb
  1446.  
  1447.     l = []
  1448.     if not doseq:
  1449.         for k, v in query:
  1450.             k = quote_plus(str(k))
  1451.             v = quote_plus(str(v))
  1452.             l.append(k + '=' + v)
  1453.         
  1454.     else:
  1455.         for k, v in query:
  1456.             k = quote_plus(str(k))
  1457.             if isinstance(v, str):
  1458.                 v = quote_plus(v)
  1459.                 l.append(k + '=' + v)
  1460.                 continue
  1461.             if _is_unicode(v):
  1462.                 v = quote_plus(v.encode('ASCII', 'replace'))
  1463.                 l.append(k + '=' + v)
  1464.                 continue
  1465.             
  1466.             try:
  1467.                 x = len(v)
  1468.             except TypeError:
  1469.                 v = quote_plus(str(v))
  1470.                 l.append(k + '=' + v)
  1471.                 continue
  1472.  
  1473.             for elt in v:
  1474.                 l.append(k + '=' + quote_plus(str(elt)))
  1475.             
  1476.         
  1477.     return '&'.join(l)
  1478.  
  1479.  
  1480. def getproxies_environment():
  1481.     '''Return a dictionary of scheme -> proxy server URL mappings.
  1482.  
  1483.     Scan the environment for variables named <scheme>_proxy;
  1484.     this seems to be the standard convention.  If you need a
  1485.     different way, you can pass a proxies dictionary to the
  1486.     [Fancy]URLopener constructor.
  1487.  
  1488.     '''
  1489.     proxies = { }
  1490.     for name, value in os.environ.items():
  1491.         name = name.lower()
  1492.         if value and name[-6:] == '_proxy':
  1493.             proxies[name[:-6]] = value
  1494.             continue
  1495.     
  1496.     return proxies
  1497.  
  1498.  
  1499. def proxy_bypass_environment(host):
  1500.     """Test if proxies should not be used for a particular host.
  1501.  
  1502.     Checks the environment for a variable named no_proxy, which should
  1503.     be a list of DNS suffixes separated by commas, or '*' for all hosts.
  1504.     """
  1505.     if not os.environ.get('no_proxy', ''):
  1506.         pass
  1507.     no_proxy = os.environ.get('NO_PROXY', '')
  1508.     if no_proxy == '*':
  1509.         return 1
  1510.     (hostonly, port) = splitport(host)
  1511.     for name in no_proxy.split(','):
  1512.         if name:
  1513.             if hostonly.endswith(name) or host.endswith(name):
  1514.                 return 1
  1515.     return 0
  1516.  
  1517. if sys.platform == 'darwin':
  1518.     
  1519.     def _CFSetup(sc):
  1520.         c_int32 = c_int32
  1521.         c_void_p = c_void_p
  1522.         c_char_p = c_char_p
  1523.         c_int = c_int
  1524.         import ctypes
  1525.         sc.CFStringCreateWithCString.argtypes = [
  1526.             c_void_p,
  1527.             c_char_p,
  1528.             c_int32]
  1529.         sc.CFStringCreateWithCString.restype = c_void_p
  1530.         sc.SCDynamicStoreCopyProxies.argtypes = [
  1531.             c_void_p]
  1532.         sc.SCDynamicStoreCopyProxies.restype = c_void_p
  1533.         sc.CFDictionaryGetValue.argtypes = [
  1534.             c_void_p,
  1535.             c_void_p]
  1536.         sc.CFDictionaryGetValue.restype = c_void_p
  1537.         sc.CFStringGetLength.argtypes = [
  1538.             c_void_p]
  1539.         sc.CFStringGetLength.restype = c_int32
  1540.         sc.CFStringGetCString.argtypes = [
  1541.             c_void_p,
  1542.             c_char_p,
  1543.             c_int32,
  1544.             c_int32]
  1545.         sc.CFStringGetCString.restype = c_int32
  1546.         sc.CFNumberGetValue.argtypes = [
  1547.             c_void_p,
  1548.             c_int,
  1549.             c_void_p]
  1550.         sc.CFNumberGetValue.restype = c_int32
  1551.         sc.CFRelease.argtypes = [
  1552.             c_void_p]
  1553.         sc.CFRelease.restype = None
  1554.  
  1555.     
  1556.     def _CStringFromCFString(sc, value):
  1557.         create_string_buffer = create_string_buffer
  1558.         import ctypes
  1559.         length = sc.CFStringGetLength(value) + 1
  1560.         buff = create_string_buffer(length)
  1561.         sc.CFStringGetCString(value, buff, length, 0)
  1562.         return buff.value
  1563.  
  1564.     
  1565.     def _CFNumberToInt32(sc, cfnum):
  1566.         byref = byref
  1567.         c_int = c_int
  1568.         import ctypes
  1569.         val = c_int()
  1570.         kCFNumberSInt32Type = 3
  1571.         sc.CFNumberGetValue(cfnum, kCFNumberSInt32Type, byref(val))
  1572.         return val.value
  1573.  
  1574.     
  1575.     def proxy_bypass_macosx_sysconf(host):
  1576.         """
  1577.         Return True iff this host shouldn't be accessed using a proxy
  1578.  
  1579.         This function uses the MacOSX framework SystemConfiguration
  1580.         to fetch the proxy information.
  1581.         """
  1582.         cdll = cdll
  1583.         import ctypes
  1584.         find_library = find_library
  1585.         import ctypes.util
  1586.         import re
  1587.         import socket
  1588.         fnmatch = fnmatch
  1589.         import fnmatch
  1590.         
  1591.         def ip2num(ipAddr):
  1592.             parts = ipAddr.split('.')
  1593.             parts = map(int, parts)
  1594.             if len(parts) != 4:
  1595.                 parts = parts + [
  1596.                     0,
  1597.                     0,
  1598.                     0,
  1599.                     0][:4]
  1600.             
  1601.             return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3]
  1602.  
  1603.         sc = cdll.LoadLibrary(find_library('SystemConfiguration'))
  1604.         _CFSetup(sc)
  1605.         hostIP = None
  1606.         if not sc:
  1607.             return False
  1608.         kSCPropNetProxiesExceptionsList = sc.CFStringCreateWithCString(0, 'ExceptionsList', 0)
  1609.         kSCPropNetProxiesExcludeSimpleHostnames = sc.CFStringCreateWithCString(0, 'ExcludeSimpleHostnames', 0)
  1610.         proxyDict = sc.SCDynamicStoreCopyProxies(None)
  1611.         if proxyDict is None:
  1612.             return False
  1613.         
  1614.         try:
  1615.             exceptions = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesExceptionsList)
  1616.             if exceptions:
  1617.                 for index in xrange(sc.CFArrayGetCount(exceptions)):
  1618.                     value = sc.CFArrayGetValueAtIndex(exceptions, index)
  1619.                     if not value:
  1620.                         continue
  1621.                     
  1622.                     value = _CStringFromCFString(sc, value)
  1623.                     m = re.match('(\\d+(?:\\.\\d+)*)(/\\d+)?', value)
  1624.                     if m is not None:
  1625.                         if hostIP is None:
  1626.                             hostIP = socket.gethostbyname(host)
  1627.                             hostIP = ip2num(hostIP)
  1628.                         
  1629.                         base = ip2num(m.group(1))
  1630.                         mask = int(m.group(2)[1:])
  1631.                         mask = 32 - mask
  1632.                         if hostIP >> mask == base >> mask:
  1633.                             return True
  1634.                         continue
  1635.                     hostIP >> mask == base >> mask
  1636.                     if fnmatch(host, value):
  1637.                         return True
  1638.                 
  1639.             
  1640.             return False
  1641.         finally:
  1642.             sc.CFRelease(kSCPropNetProxiesExceptionsList)
  1643.             sc.CFRelease(kSCPropNetProxiesExcludeSimpleHostnames)
  1644.  
  1645.  
  1646.     
  1647.     def getproxies_macosx_sysconf():
  1648.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1649.  
  1650.         This function uses the MacOSX framework SystemConfiguration
  1651.         to fetch the proxy information.
  1652.         '''
  1653.         cdll = cdll
  1654.         import ctypes
  1655.         find_library = find_library
  1656.         import ctypes.util
  1657.         sc = cdll.LoadLibrary(find_library('SystemConfiguration'))
  1658.         _CFSetup(sc)
  1659.         if not sc:
  1660.             return { }
  1661.         kSCPropNetProxiesHTTPEnable = sc.CFStringCreateWithCString(0, 'HTTPEnable', 0)
  1662.         kSCPropNetProxiesHTTPProxy = sc.CFStringCreateWithCString(0, 'HTTPProxy', 0)
  1663.         kSCPropNetProxiesHTTPPort = sc.CFStringCreateWithCString(0, 'HTTPPort', 0)
  1664.         kSCPropNetProxiesHTTPSEnable = sc.CFStringCreateWithCString(0, 'HTTPSEnable', 0)
  1665.         kSCPropNetProxiesHTTPSProxy = sc.CFStringCreateWithCString(0, 'HTTPSProxy', 0)
  1666.         kSCPropNetProxiesHTTPSPort = sc.CFStringCreateWithCString(0, 'HTTPSPort', 0)
  1667.         kSCPropNetProxiesFTPEnable = sc.CFStringCreateWithCString(0, 'FTPEnable', 0)
  1668.         kSCPropNetProxiesFTPPassive = sc.CFStringCreateWithCString(0, 'FTPPassive', 0)
  1669.         kSCPropNetProxiesFTPPort = sc.CFStringCreateWithCString(0, 'FTPPort', 0)
  1670.         kSCPropNetProxiesFTPProxy = sc.CFStringCreateWithCString(0, 'FTPProxy', 0)
  1671.         kSCPropNetProxiesGopherEnable = sc.CFStringCreateWithCString(0, 'GopherEnable', 0)
  1672.         kSCPropNetProxiesGopherPort = sc.CFStringCreateWithCString(0, 'GopherPort', 0)
  1673.         kSCPropNetProxiesGopherProxy = sc.CFStringCreateWithCString(0, 'GopherProxy', 0)
  1674.         proxies = { }
  1675.         proxyDict = sc.SCDynamicStoreCopyProxies(None)
  1676.         
  1677.         try:
  1678.             enabled = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPEnable)
  1679.             if enabled and _CFNumberToInt32(sc, enabled):
  1680.                 proxy = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPProxy)
  1681.                 port = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPPort)
  1682.                 if proxy:
  1683.                     proxy = _CStringFromCFString(sc, proxy)
  1684.                     if port:
  1685.                         port = _CFNumberToInt32(sc, port)
  1686.                         proxies['http'] = 'http://%s:%i' % (proxy, port)
  1687.                     else:
  1688.                         proxies['http'] = 'http://%s' % (proxy,)
  1689.                 
  1690.             
  1691.             enabled = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPSEnable)
  1692.             if enabled and _CFNumberToInt32(sc, enabled):
  1693.                 proxy = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPSProxy)
  1694.                 port = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesHTTPSPort)
  1695.                 if proxy:
  1696.                     proxy = _CStringFromCFString(sc, proxy)
  1697.                     if port:
  1698.                         port = _CFNumberToInt32(sc, port)
  1699.                         proxies['https'] = 'http://%s:%i' % (proxy, port)
  1700.                     else:
  1701.                         proxies['https'] = 'http://%s' % (proxy,)
  1702.                 
  1703.             
  1704.             enabled = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesFTPEnable)
  1705.             if enabled and _CFNumberToInt32(sc, enabled):
  1706.                 proxy = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesFTPProxy)
  1707.                 port = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesFTPPort)
  1708.                 if proxy:
  1709.                     proxy = _CStringFromCFString(sc, proxy)
  1710.                     if port:
  1711.                         port = _CFNumberToInt32(sc, port)
  1712.                         proxies['ftp'] = 'http://%s:%i' % (proxy, port)
  1713.                     else:
  1714.                         proxies['ftp'] = 'http://%s' % (proxy,)
  1715.                 
  1716.             
  1717.             enabled = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesGopherEnable)
  1718.             if enabled and _CFNumberToInt32(sc, enabled):
  1719.                 proxy = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesGopherProxy)
  1720.                 port = sc.CFDictionaryGetValue(proxyDict, kSCPropNetProxiesGopherPort)
  1721.                 if proxy:
  1722.                     proxy = _CStringFromCFString(sc, proxy)
  1723.                     if port:
  1724.                         port = _CFNumberToInt32(sc, port)
  1725.                         proxies['gopher'] = 'http://%s:%i' % (proxy, port)
  1726.                     else:
  1727.                         proxies['gopher'] = 'http://%s' % (proxy,)
  1728.                 
  1729.         finally:
  1730.             sc.CFRelease(proxyDict)
  1731.  
  1732.         sc.CFRelease(kSCPropNetProxiesHTTPEnable)
  1733.         sc.CFRelease(kSCPropNetProxiesHTTPProxy)
  1734.         sc.CFRelease(kSCPropNetProxiesHTTPPort)
  1735.         sc.CFRelease(kSCPropNetProxiesFTPEnable)
  1736.         sc.CFRelease(kSCPropNetProxiesFTPPassive)
  1737.         sc.CFRelease(kSCPropNetProxiesFTPPort)
  1738.         sc.CFRelease(kSCPropNetProxiesFTPProxy)
  1739.         sc.CFRelease(kSCPropNetProxiesGopherEnable)
  1740.         sc.CFRelease(kSCPropNetProxiesGopherPort)
  1741.         sc.CFRelease(kSCPropNetProxiesGopherProxy)
  1742.         return proxies
  1743.  
  1744.     
  1745.     def proxy_bypass(host):
  1746.         if getproxies_environment():
  1747.             return proxy_bypass_environment(host)
  1748.         return proxy_bypass_macosx_sysconf(host)
  1749.  
  1750.     
  1751.     def getproxies():
  1752.         if not getproxies_environment():
  1753.             pass
  1754.         return getproxies_macosx_sysconf()
  1755.  
  1756. elif os.name == 'nt':
  1757.     
  1758.     def getproxies_registry():
  1759.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1760.  
  1761.         Win32 uses the registry to store proxies.
  1762.  
  1763.         '''
  1764.         proxies = { }
  1765.         
  1766.         try:
  1767.             import _winreg
  1768.         except ImportError:
  1769.             return proxies
  1770.  
  1771.         
  1772.         try:
  1773.             internetSettings = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings')
  1774.             proxyEnable = _winreg.QueryValueEx(internetSettings, 'ProxyEnable')[0]
  1775.             if proxyEnable:
  1776.                 proxyServer = str(_winreg.QueryValueEx(internetSettings, 'ProxyServer')[0])
  1777.                 if '=' in proxyServer:
  1778.                     for p in proxyServer.split(';'):
  1779.                         (protocol, address) = p.split('=', 1)
  1780.                         import re
  1781.                         if not re.match('^([^/:]+)://', address):
  1782.                             address = '%s://%s' % (protocol, address)
  1783.                         
  1784.                         proxies[protocol] = address
  1785.                     
  1786.                 elif proxyServer[:5] == 'http:':
  1787.                     proxies['http'] = proxyServer
  1788.                 else:
  1789.                     proxies['http'] = 'http://%s' % proxyServer
  1790.                     proxies['ftp'] = 'ftp://%s' % proxyServer
  1791.             
  1792.             internetSettings.Close()
  1793.         except (WindowsError, ValueError, TypeError):
  1794.             pass
  1795.  
  1796.         return proxies
  1797.  
  1798.     
  1799.     def getproxies():
  1800.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1801.  
  1802.         Returns settings gathered from the environment, if specified,
  1803.         or the registry.
  1804.  
  1805.         '''
  1806.         if not getproxies_environment():
  1807.             pass
  1808.         return getproxies_registry()
  1809.  
  1810.     
  1811.     def proxy_bypass_registry(host):
  1812.         
  1813.         try:
  1814.             import _winreg
  1815.             import re
  1816.         except ImportError:
  1817.             return 0
  1818.  
  1819.         
  1820.         try:
  1821.             internetSettings = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings')
  1822.             proxyEnable = _winreg.QueryValueEx(internetSettings, 'ProxyEnable')[0]
  1823.             proxyOverride = str(_winreg.QueryValueEx(internetSettings, 'ProxyOverride')[0])
  1824.         except WindowsError:
  1825.             return 0
  1826.  
  1827.         if not proxyEnable or not proxyOverride:
  1828.             return 0
  1829.         (rawHost, port) = splitport(host)
  1830.         host = [
  1831.             rawHost]
  1832.         
  1833.         try:
  1834.             addr = socket.gethostbyname(rawHost)
  1835.             if addr != rawHost:
  1836.                 host.append(addr)
  1837.         except socket.error:
  1838.             not proxyOverride
  1839.             not proxyOverride
  1840.         except:
  1841.             not proxyOverride
  1842.  
  1843.         
  1844.         try:
  1845.             fqdn = socket.getfqdn(rawHost)
  1846.             if fqdn != rawHost:
  1847.                 host.append(fqdn)
  1848.         except socket.error:
  1849.             not proxyOverride
  1850.             not proxyOverride
  1851.         except:
  1852.             not proxyOverride
  1853.  
  1854.         proxyOverride = proxyOverride.split(';')
  1855.         i = 0
  1856.         while i < len(proxyOverride):
  1857.             if proxyOverride[i] == '<local>':
  1858.                 proxyOverride[i:i + 1] = [
  1859.                     'localhost',
  1860.                     '127.0.0.1',
  1861.                     socket.gethostname(),
  1862.                     socket.gethostbyname(socket.gethostname())]
  1863.             
  1864.             i += 1
  1865.         for test in proxyOverride:
  1866.             test = test.replace('.', '\\.')
  1867.             test = test.replace('*', '.*')
  1868.             test = test.replace('?', '.')
  1869.             for val in host:
  1870.                 if re.match(test, val, re.I):
  1871.                     return 1
  1872.             
  1873.         
  1874.         return 0
  1875.  
  1876.     
  1877.     def proxy_bypass(host):
  1878.         '''Return a dictionary of scheme -> proxy server URL mappings.
  1879.  
  1880.         Returns settings gathered from the environment, if specified,
  1881.         or the registry.
  1882.  
  1883.         '''
  1884.         if getproxies_environment():
  1885.             return proxy_bypass_environment(host)
  1886.         return proxy_bypass_registry(host)
  1887.  
  1888. else:
  1889.     getproxies = getproxies_environment
  1890.     proxy_bypass = proxy_bypass_environment
  1891.  
  1892. def test1():
  1893.     s = ''
  1894.     for i in range(256):
  1895.         s = s + chr(i)
  1896.     
  1897.     s = s * 4
  1898.     t0 = time.time()
  1899.     qs = quote(s)
  1900.     uqs = unquote(qs)
  1901.     t1 = time.time()
  1902.     if uqs != s:
  1903.         print 'Wrong!'
  1904.     
  1905.     print repr(s)
  1906.     print repr(qs)
  1907.     print repr(uqs)
  1908.     print round(t1 - t0, 3), 'sec'
  1909.  
  1910.  
  1911. def reporthook(blocknum, blocksize, totalsize):
  1912.     print 'Block number: %d, Block size: %d, Total size: %d' % (blocknum, blocksize, totalsize)
  1913.  
  1914.  
  1915. def test(args = []):
  1916.     if not args:
  1917.         args = [
  1918.             '/etc/passwd',
  1919.             'file:/etc/passwd',
  1920.             'file://localhost/etc/passwd',
  1921.             'ftp://ftp.gnu.org/pub/README',
  1922.             'http://www.python.org/index.html']
  1923.         if hasattr(URLopener, 'open_https'):
  1924.             args.append('https://synergy.as.cmu.edu/~geek/')
  1925.         
  1926.     
  1927.     
  1928.     try:
  1929.         for url in args:
  1930.             print '----------', url, '----------'
  1931.             (fn, h) = urlretrieve(url, None, reporthook)
  1932.             print fn
  1933.             if h:
  1934.                 print '======'
  1935.                 for k in h.keys():
  1936.                     print k + ':', h[k]
  1937.                 
  1938.                 print '======'
  1939.             
  1940.             fp = open(fn, 'rb')
  1941.             data = fp.read()
  1942.             del fp
  1943.             if '\r' in data:
  1944.                 table = string.maketrans('', '')
  1945.                 data = data.translate(table, '\r')
  1946.             
  1947.             print data
  1948.             (fn, h) = (None, None)
  1949.         
  1950.         print '-' * 40
  1951.     finally:
  1952.         urlcleanup()
  1953.  
  1954.  
  1955.  
  1956. def main():
  1957.     import getopt
  1958.     import sys
  1959.     
  1960.     try:
  1961.         (opts, args) = getopt.getopt(sys.argv[1:], 'th')
  1962.     except getopt.error:
  1963.         msg = None
  1964.         print msg
  1965.         print 'Use -h for help'
  1966.         return None
  1967.  
  1968.     t = 0
  1969.     for o, a in opts:
  1970.         if o == '-t':
  1971.             t = t + 1
  1972.         
  1973.         if o == '-h':
  1974.             print 'Usage: python urllib.py [-t] [url ...]'
  1975.             print '-t runs self-test;', 'otherwise, contents of urls are printed'
  1976.             return None
  1977.     
  1978.     if t:
  1979.         if t > 1:
  1980.             test1()
  1981.         
  1982.         test(args)
  1983.     elif not args:
  1984.         print 'Use -h for help'
  1985.     
  1986.     for url in args:
  1987.         print urlopen(url).read(),
  1988.     
  1989.  
  1990. if __name__ == '__main__':
  1991.     main()
  1992.  
  1993.